Emit more info on --message-format=json
authorAlex Crichton <alex@alexcrichton.com>
Wed, 23 Nov 2016 16:29:36 +0000 (08:29 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Fri, 2 Dec 2016 18:00:19 +0000 (10:00 -0800)
This adds more output on Cargo's behalf to the output of
`--message-format=json`. Cargo will now emit a message when a crate is finished
compiling with a list of all files that were just generated along with a message
when a build script finishes executing with linked libraries and linked library
paths.

Closes #3212

src/cargo/core/manifest.rs
src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_rustc/custom_build.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/util/machine_message.rs
tests/build.rs

index 93fac21fdf971ff68122ba5ba4cc9677c9da0d0f..da40c6a55a0b79366eda828a174d7ef08dea4db8 100644 (file)
@@ -123,7 +123,7 @@ impl Encodable for TargetKind {
     }
 }
 
-#[derive(RustcEncodable, RustcDecodable, Clone, PartialEq, Eq, Debug, Hash)]
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub struct Profile {
     pub opt_level: String,
     pub lto: bool,
@@ -139,6 +139,25 @@ pub struct Profile {
     pub panic: Option<String>,
 }
 
+#[derive(RustcEncodable)]
+struct SerializedProfile<'a> {
+    opt_level: &'a str,
+    debuginfo: bool,
+    debug_assertions: bool,
+    test: bool,
+}
+
+impl Encodable for Profile {
+    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        SerializedProfile {
+            opt_level: &self.opt_level,
+            debuginfo: self.debuginfo,
+            debug_assertions: self.debug_assertions,
+            test: self.test,
+        }.encode(s)
+    }
+}
+
 #[derive(Default, Clone, Debug, PartialEq, Eq)]
 pub struct Profiles {
     pub release: Profile,
index 74b787694763d1c02558c6c1fe3bd38ee5bcaa22..e2f62e2593b9366004ad7801c63107a32426d6f4 100644 (file)
@@ -251,7 +251,7 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>,
         let mut build_config = scrape_build_config(config, jobs, target)?;
         build_config.release = release;
         build_config.test = mode == CompileMode::Test || mode == CompileMode::Bench;
-        build_config.json_errors = message_format == MessageFormat::Json;
+        build_config.json_messages = message_format == MessageFormat::Json;
         if let CompileMode::Doc { deps } = mode {
             build_config.doc_all = deps;
         }
@@ -512,7 +512,7 @@ fn scrape_target_config(config: &Config, triple: &str)
                     let (flags, definition) = value.string(&k)?;
                     let whence = format!("in `{}` (in {})", key,
                                          definition.display());
-                    let (paths, links) = 
+                    let (paths, links) =
                         BuildOutput::parse_rustc_flags(&flags, &whence)
                     ?;
                     output.library_paths.extend(paths);
index eb62f38c522b1ec22ced825d4b344cf94bd9706b..e412b846ed9073b888facf352d7cad1cc2f313ed 100644 (file)
@@ -7,6 +7,7 @@ use std::sync::{Mutex, Arc};
 use core::PackageId;
 use util::{CargoResult, Human, Freshness, Cfg};
 use util::{internal, ChainError, profile, paths};
+use util::machine_message;
 
 use super::job::Work;
 use super::{fingerprint, Kind, Context, Unit};
@@ -168,6 +169,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>)
                output_file.clone());
     let build_scripts = super::load_build_deps(cx, unit);
     let kind = unit.kind;
+    let json_messages = cx.build_config.json_messages;
 
     // Check to see if the build script as already run, and if it has keep
     // track of whether it has told us about some explicit dependencies
@@ -242,6 +244,19 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>)
         // state informing what variables were discovered via our script as
         // well.
         let parsed_output = BuildOutput::parse(&output.stdout, &pkg_name)?;
+
+        if json_messages {
+            let library_paths = parsed_output.library_paths.iter().map(|l| {
+                l.display().to_string()
+            }).collect::<Vec<_>>();
+            machine_message::emit(machine_message::BuildScript {
+                package_id: &id,
+                linked_libs: &parsed_output.library_links,
+                linked_paths: &library_paths,
+                cfgs: &parsed_output.cfgs,
+            });
+        }
+
         build_state.insert(id, kind, parsed_output);
         Ok(())
     });
index e54f539f39279110f27fed3e3d54939817f4786f..186ce0d728cdef7d8aeda8e22176036511ff1100 100644 (file)
@@ -42,7 +42,7 @@ pub struct BuildConfig {
     pub release: bool,
     pub test: bool,
     pub doc_all: bool,
-    pub json_errors: bool,
+    pub json_messages: bool,
 }
 
 #[derive(Clone, Default)]
@@ -246,9 +246,15 @@ fn rustc(cx: &mut Context, unit: &Unit) -> CargoResult<Work> {
     let cwd = cx.config.cwd().to_path_buf();
 
     rustc.args(&cx.rustflags_args(unit)?);
-    let json_errors = cx.build_config.json_errors;
+    let json_messages = cx.build_config.json_messages;
     let package_id = unit.pkg.package_id().clone();
     let target = unit.target.clone();
+    let profile = unit.profile.clone();
+    let features = cx.resolve.features(unit.pkg.package_id())
+                     .into_iter()
+                     .flat_map(|i| i)
+                     .map(|s| s.to_string())
+                     .collect::<Vec<_>>();
     return Ok(Work::new(move |state| {
         // Only at runtime have we discovered what the extra -L and -l
         // arguments are for native libraries, so we process those here. We
@@ -273,7 +279,7 @@ fn rustc(cx: &mut Context, unit: &Unit) -> CargoResult<Work> {
         }
 
         state.running(&rustc);
-        if json_errors {
+        if json_messages {
             rustc.exec_with_streaming(
                 &mut |line| if !line.is_empty() {
                     Err(internal(&format!("compiler stdout is not empty: `{}`", line)))
@@ -285,12 +291,11 @@ fn rustc(cx: &mut Context, unit: &Unit) -> CargoResult<Work> {
                         internal(&format!("compiler produced invalid json: `{}`", line))
                     })?;
 
-                    machine_message::FromCompiler::new(
-                        &package_id,
-                        &target,
-                        compiler_message
-                    ).emit();
-
+                    machine_message::emit(machine_message::FromCompiler {
+                        package_id: &package_id,
+                        target: &target,
+                        message: compiler_message,
+                    });
                     Ok(())
                 },
             ).map(|_| ())
@@ -321,6 +326,18 @@ fn rustc(cx: &mut Context, unit: &Unit) -> CargoResult<Work> {
             fingerprint::append_current_dir(&dep_info_loc, &cwd)?;
         }
 
+        if json_messages {
+            machine_message::emit(machine_message::Artifact {
+                package_id: &package_id,
+                target: &target,
+                profile: &profile,
+                features: features,
+                filenames: filenames.iter().map(|&(ref src, _, _)| {
+                    src.display().to_string()
+                }).collect(),
+            });
+        }
+
         Ok(())
     }));
 
@@ -527,7 +544,7 @@ fn build_base_args(cx: &mut Context,
         cmd.arg("--color").arg(&color_config.to_string());
     }
 
-    if cx.build_config.json_errors {
+    if cx.build_config.json_messages {
         cmd.arg("--error-format").arg("json");
     }
 
index 7de1ead0e671c015d3ccc5d11789a2df8f031b0c..64e6c35f1606ad797b03749fb5031bc225436b6a 100644 (file)
@@ -1,30 +1,60 @@
-use rustc_serialize::json;
-use core::{PackageId, Target};
+use rustc_serialize::Encodable;
+use rustc_serialize::json::{self, Json};
+
+use core::{PackageId, Target, Profile};
+
+pub trait Message: Encodable {
+    fn reason(&self) -> &str;
+}
+
+pub fn emit<T: Message>(t: T) {
+    let json = json::encode(&t).unwrap();
+    let mut map = match json.parse().unwrap() {
+        Json::Object(obj) => obj,
+        _ => panic!("not a json object"),
+    };
+    map.insert("reason".to_string(), Json::String(t.reason().to_string()));
+    println!("{}", Json::Object(map));
+}
 
 #[derive(RustcEncodable)]
 pub struct FromCompiler<'a> {
-    reason: &'static str,
-    package_id: &'a PackageId,
-    target: &'a Target,
-    message: json::Json,
-}
-
-impl<'a> FromCompiler<'a> {
-    pub fn new(package_id: &'a PackageId,
-               target: &'a Target,
-               message: json::Json)
-               -> FromCompiler<'a> {
-        FromCompiler {
-            reason: "compiler-message",
-            package_id: package_id,
-            target: target,
-            message: message,
-        }
+    pub package_id: &'a PackageId,
+    pub target: &'a Target,
+    pub message: json::Json,
+}
+
+impl<'a> Message for FromCompiler<'a> {
+    fn reason(&self) -> &str {
+        "compiler-message"
     }
+}
 
-    pub fn emit(self) {
-        let json = json::encode(&self).unwrap();
-        println!("{}", json);
+#[derive(RustcEncodable)]
+pub struct Artifact<'a> {
+    pub package_id: &'a PackageId,
+    pub target: &'a Target,
+    pub profile: &'a Profile,
+    pub features: Vec<String>,
+    pub filenames: Vec<String>,
+}
+
+impl<'a> Message for Artifact<'a> {
+    fn reason(&self) -> &str {
+        "compiler-artifact"
     }
 }
 
+#[derive(RustcEncodable)]
+pub struct BuildScript<'a> {
+    pub package_id: &'a PackageId,
+    pub linked_libs: &'a [String],
+    pub linked_paths: &'a [String],
+    pub cfgs: &'a [String],
+}
+
+impl<'a> Message for BuildScript<'a> {
+    fn reason(&self) -> &str {
+        "build-script-executed"
+    }
+}
index 02e14d18dd35235a6707a99d6a2717578b6d8a98..e6ae434c5e76e773b9ea5f09fdb9e7050419cf38 100644 (file)
@@ -2411,6 +2411,20 @@ fn compiler_json_error_format() {
         }
     }
 
+    {
+        "reason":"compiler-artifact",
+        "profile": {
+            "debug_assertions": true,
+            "debuginfo": true,
+            "opt_level": "0",
+            "test": false
+        },
+        "features": [],
+        "package_id":"bar 0.5.0 ([..])",
+        "target":{"kind":["lib"],"name":"bar","src_path":"[..]lib.rs"},
+        "filenames":["[..].rlib"]
+    }
+
     {
         "reason":"compiler-message",
         "package_id":"foo 0.5.0 ([..])",
@@ -2426,6 +2440,20 @@ fn compiler_json_error_format() {
             }]
         }
     }
+
+    {
+        "reason":"compiler-artifact",
+        "package_id":"foo 0.5.0 ([..])",
+        "target":{"kind":["bin"],"name":"foo","src_path":"[..]main.rs"},
+        "profile": {
+            "debug_assertions": true,
+            "debuginfo": true,
+            "opt_level": "0",
+            "test": false
+        },
+        "features": [],
+        "filenames": ["[..]"]
+    }
 "#));
 }